/*
 * VEML6075.c
 *
 *  Created on: Sep 4, 2020
 *      Author: daniel
 */

#include "VEML6075.h"

extern I2C_HandleTypeDef hi2c1;

#define VEML6075_I2C		hi2c1
#define VEML6075_ADDR		0x10
#define VEML6075_DEVID 		0x26

// Reading the application note on calculation of UV index, the "dummy" channel
// value is actually not a dummy value at all, but the dark current count.
// NAMES ARE IMPORTANT PEOPLE.

#define VEML6075_REG_CONF        (0x00) // Configuration register (options below)
#define VEML6075_REG_UVA         (0x07) // UVA register
#define VEML6075_REG_DUMMY       (0x08) // Dark current register (NOT DUMMY)
#define VEML6075_REG_UVB         (0x09) // UVB register
#define VEML6075_REG_UVCOMP1     (0x0A) // Visible compensation register
#define VEML6075_REG_UVCOMP2     (0x0B) // IR compensation register
#define VEML6075_REG_DEVID       (0x0C) // Device ID register

#define VEML6075_CONF_IT_50MS    (0x00) // Integration time = 50ms (default)
#define VEML6075_CONF_IT_100MS   (0x10) // Integration time = 100ms
#define VEML6075_CONF_IT_200MS   (0x20) // Integration time = 200ms
#define VEML6075_CONF_IT_400MS   (0x30) // Integration time = 400ms
#define VEML6075_CONF_IT_800MS   (0x40) // Integration time = 800ms
#define VEML6075_CONF_IT_MASK    (0x8F) // Mask off other config bits

#define VEML6075_CONF_HD_NORM    (0x00) // Normal dynamic seetting (default)
#define VEML6075_CONF_HD_HIGH    (0x08) // High dynamic seetting

#define VEML6075_CONF_TRIG       (0x04) // Trigger measurement, clears by itself

#define VEML6075_CONF_AF_OFF     (0x00) // Active force mode disabled (default)
#define VEML6075_CONF_AF_ON      (0x02) // Active force mode enabled (?)

#define VEML6075_CONF_SD_OFF     (0x00) // Power up
#define VEML6075_CONF_SD_ON      (0x01) // Power down

// To calculate the UV Index, a bunch of empirical/magical coefficients need to
// be applied to UVA and UVB readings to get a proper composite index value.
// Seems pretty hand wavey, though not nearly as annoying as the dark current
// not being subtracted out by default.

#define VEML6075_UVI_UVA_VIS_COEFF (2.22) // aka coeff "A"
#define VEML6075_UVI_UVA_IR_COEFF  (1.33) // aka coeff "B"
#define VEML6075_UVI_UVB_VIS_COEFF (2.95) // aka coeff "C"
#define VEML6075_UVI_UVB_IR_COEFF  (1.74) // aka coeff "D"

// Once the above offsets and crunching is done, there's a last weighting
// function to convert the ADC counts into the UV index values. This handles
// both the conversion into irradiance (W/m^2) and the skin erythema weighting
// by wavelength--UVB is way more dangerous than UVA, due to shorter
// wavelengths and thus more energy per photon. These values convert the
// compensated values.
//
// NB These are the "open air" values given in the application note for the
// VEML6075.

#define VEML6075_UVI_UVA_RESPONSE (0.001461)
#define VEML6075_UVI_UVB_RESPONSE (0.002591)

#define NUM_INTEGRATION_TIMES 5

//const float UVA_RESPONSIVITY[NUM_INTEGRATION_TIMES] =
//{
//        UVA_RESPONSIVITY_100MS_UNCOVERED / 0.5016286645, // 50ms
//        UVA_RESPONSIVITY_100MS_UNCOVERED,                // 100ms
//        UVA_RESPONSIVITY_100MS_UNCOVERED / 2.039087948,  // 200ms
//        UVA_RESPONSIVITY_100MS_UNCOVERED / 3.781758958,  // 400ms
//        UVA_RESPONSIVITY_100MS_UNCOVERED / 7.371335505   // 800ms
//};
//
//const float UVB_RESPONSIVITY[NUM_INTEGRATION_TIMES] =
//{
//        UVB_RESPONSIVITY_100MS_UNCOVERED / 0.5016286645, // 50ms
//        UVB_RESPONSIVITY_100MS_UNCOVERED,                // 100ms
//        UVB_RESPONSIVITY_100MS_UNCOVERED / 2.039087948,  // 200ms
//        UVB_RESPONSIVITY_100MS_UNCOVERED / 3.781758958,  // 400ms
//        UVB_RESPONSIVITY_100MS_UNCOVERED / 7.371335505   // 800ms
//};


#ifndef max
#define max(a,b)            (((a) > (b)) ? (a) : (b))
#endif

bool VEML6075_Measure()
{
    HAL_StatusTypeDef	status = 0;
    uint8_t				buffer[3];
    buffer[0] = VEML6075_REG_CONF;
    buffer[1] = (1 << 4) | (0 << 3) | (1 << 2) | (1 << 1);		// IT:100ms; HD:0; UV+Trig: 1   AF=1 SD=0
    buffer[2] = 0;

	status = HAL_I2C_Master_Transmit(&VEML6075_I2C, VEML6075_ADDR<<1, buffer, 3, 100);

	return (status == HAL_OK);
}

bool VEML6075_GetUV(float* uva, float* uvb, float* uvi)
{
    HAL_StatusTypeDef	status = 0;
    uint8_t				buffer[3];
    uint16_t 			raw_uva;
    uint16_t 			raw_uvb;
    uint16_t 			raw_dark;
    uint16_t 			raw_vis;
    uint16_t 			raw_ir;

    *uva = 0;
    *uvb = 0;
    *uvi = 0;

    // UVA
    status = HAL_I2C_Mem_Read(&VEML6075_I2C, VEML6075_ADDR<<1, VEML6075_REG_UVA, I2C_MEMADD_SIZE_8BIT, buffer, 2, 100);
	if (status == HAL_OK)
	{
		raw_uva = (buffer[0] & 0x00FF) | ((buffer[1] & 0x00FF) << 8);
	}
	else
	{
		return false;
	}

	// DUMMY
    status = HAL_I2C_Mem_Read(&VEML6075_I2C, VEML6075_ADDR<<1, VEML6075_REG_DUMMY, I2C_MEMADD_SIZE_8BIT, buffer, 2, 100);
	if (status == HAL_OK)
	{
		raw_dark = (buffer[0] & 0x00FF) | ((buffer[1] & 0x00FF) << 8);
	}
	else
	{
		return false;
	}

	// UVB
	status = HAL_I2C_Mem_Read(&VEML6075_I2C, VEML6075_ADDR<<1, VEML6075_REG_UVB, I2C_MEMADD_SIZE_8BIT, buffer, 2, 100);
	if (status == HAL_OK)
	{
		raw_uvb = (buffer[0] & 0x00FF) | ((buffer[1] & 0x00FF) << 8);
	}
	else
	{
		return false;
	}

	// UVCOMP1
	status = HAL_I2C_Mem_Read(&VEML6075_I2C, VEML6075_ADDR<<1, VEML6075_REG_UVCOMP1, I2C_MEMADD_SIZE_8BIT, buffer, 2, 100);	if (status == HAL_OK)
	{
		raw_vis = (buffer[0] & 0x00FF) | ((buffer[1] & 0x00FF) << 8);
	}
	else
	{
		return false;
	}

	// UVCOMP2
	status = HAL_I2C_Mem_Read(&VEML6075_I2C, VEML6075_ADDR<<1, VEML6075_REG_UVCOMP2, I2C_MEMADD_SIZE_8BIT, buffer, 2, 100);	if (status == HAL_OK)
	{
		raw_ir = (buffer[0] & 0x00FF) | ((buffer[1] & 0x00FF) << 8);
	}
	else
	{
		return false;
	}

	// 读取结束后，shut down
    buffer[0] = VEML6075_REG_CONF;
    buffer[1] = (1 << 4) | (0 << 3) | (1 << 2) | (1 << 1) | 1;		// shutdown = 1
    buffer[2] = 0;
	status = HAL_I2C_Master_Transmit(&VEML6075_I2C, VEML6075_ADDR<<1, buffer, 3, 100);

	float comp_vis, temp_vis;
	float comp_ir, temp_ir;
	float comp_uva;
	float comp_uvb;

	comp_ir  = max(raw_ir - raw_dark, 0);
	comp_vis = max(raw_vis - raw_dark, 0);
	comp_uva = max(raw_uva - raw_dark, 0);
	comp_uvb = max(raw_uvb - raw_dark, 0);

	// Scale by coeffs from datasheet
	temp_vis = comp_vis * VEML6075_UVI_UVA_VIS_COEFF;
	temp_ir  = comp_ir * VEML6075_UVI_UVA_IR_COEFF;

	// Subtract out visible and IR components
	comp_uva = max(comp_uva - temp_ir, 0.0F);
	comp_uva = max(comp_uva - temp_vis, 0.0F);

	*uva = comp_uva;

	// Scale by coeffs from datasheet
	temp_vis = comp_vis * VEML6075_UVI_UVB_VIS_COEFF;
	temp_ir  = comp_ir * VEML6075_UVI_UVB_IR_COEFF;

	// Subtract out visible and IR components
	comp_uvb = max(comp_uvb - temp_ir, 0.0F);
	comp_uvb = max(comp_uvb - temp_vis, 0.0F);

	*uvb = comp_uvb;

	// 计算UV Index
    float index = 0.0;

    float uva_weighted = comp_uva * VEML6075_UVI_UVA_RESPONSE;
    float uvb_weighted = comp_uvb * VEML6075_UVI_UVB_RESPONSE;
    index = (uva_weighted + uvb_weighted) / 2.0;
    *uvi = index;

    return true;
}
